﻿using Devonline.Core;
using Devonline.Http;
using IdentityServer4.EntityFramework.Storage;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.NewtonsoftJson;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using StackExchange.Redis;

namespace Devonline.Identity.Admin
{
    public class Startup
    {
        public IWebHostEnvironment Environment { get; }
        public IConfiguration Configuration { get; }

        public Startup(IWebHostEnvironment environment, IConfiguration configuration)
        {
            Environment = environment;
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            var appSetting = services.AddAppSettings<AdminSetting>();

            services.AddLogging();
            services.AddControllers()
                .AddOData(options => options.Count().Filter().Expand().Select().OrderBy().SetMaxTop(100)
                .AddRouteComponents("odata", Utility.BuildEdmModel()))
                .AddODataNewtonsoftJson()
                .AddJsonOptions();
#if DEBUG
            services.AddControllersWithViews().AddRazorRuntimeCompilation();
#else
            services.AddControllersWithViews();
#endif

            var dataProtection = appSetting.DataProtection;
            services.AddSingleton(dataProtection);
            services.AddDataProtection(x => x.ApplicationDiscriminator = dataProtection.ApplicationDiscriminator)
                .SetApplicationName(dataProtection.ApplicationName)
                .PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(dataProtection.Redis), dataProtection.KeyName);

            var assemblyName = typeof(Startup).Assembly.FullName;
            services.AddDbContext<IdentityDbContext>(options => options.UseNpgsql(appSetting.IdentityDbContext, sql => sql.MigrationsAssembly(assemblyName)));
            services.AddConfigurationDbContext(options => options.ConfigureDbContext = db => db.UseNpgsql(appSetting.IdentityDbContext, sql => sql.MigrationsAssembly(assemblyName)));
            services.AddOperationalDbContext(options => options.ConfigureDbContext = db => db.UseNpgsql(appSetting.IdentityDbContext, sql => sql.MigrationsAssembly(assemblyName)));

            services.AddIdentity<User, Role>()
                .AddEntityFrameworkStores<IdentityDbContext>()
                .AddPersonalDataProtection<AesProtector, DefaultKeyRing>()
                .AddDefaultTokenProviders();

            services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;
                options.EmitStaticAudienceClaim = true;
                options.UserInteraction = appSetting.UserInteraction;
            })
            .AddAspNetIdentity<User>()
            .AddConfigurationStore(options => options.ConfigureDbContext = db => db.UseNpgsql(appSetting.IdentityDbContext, sql => sql.MigrationsAssembly(assemblyName)))
            .AddOperationalStore(options =>
            {
                options.ConfigureDbContext = db => db.UseNpgsql(appSetting.IdentityDbContext, sql => sql.MigrationsAssembly(assemblyName));
                options.EnableTokenCleanup = true;
            })
            .AddDeveloperSigningCredential();

            var auth = services.AddAuthentication();
            if (appSetting.GitHub.Enable)
            {
                auth.AddGitHub(options => appSetting.GitHub.Configure(options));
            }

            if (appSetting.Weixin.Enable)
            {
                auth.AddWeixin(options => appSetting.Weixin.Configure(options));
            }

            if (appSetting.Alipay.Enable)
            {
                auth.AddAlipay(options => appSetting.Alipay.Configure(options));
            }

            if (appSetting.QQ.Enable)
            {
                auth.AddQQ(options => appSetting.QQ.Configure(options));
            }

            services.AddTransient<DbContext, IdentityDbContext>();
            services.AddTransient<UserStore>();
            services.AddTransient<RoleStore>();
            services.AddTransient<ResourceAccessStore>();
        }

        public void Configure(IApplicationBuilder app)
        {
            if (Environment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
                app.UseHttpsRedirection();
            }

            app.UseRouting();
            app.UseStaticFiles();
            app.UseIdentityServer();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers().RequireAuthorization();
                endpoints.MapDefaultControllerRoute().RequireAuthorization();
            });
        }
    }
}